﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using Epitech.Krisk.Kinect.Sensor.Gestures;
using Epitech.Krisk.Kinect.Sensor.Stability;
using Epitech.Krisk.Kinect.Sensor.Tools;
using Microsoft.Research.Kinect.Nui;

namespace Epitech.Krisk.Kinect.Sensor.Zoom
{
    public class ZoomManager : INotifyPropertyChanged
    {
        private bool _isZoomActive = false;

        public bool IsZoomActive
        {
            get { return _isZoomActive; }
            set
            {
                _isZoomActive = value;
                OnPropertyChanged("IsZoomActive");
            }
        }

        public event Action<double> ZoomEvent;

        private bool _hasbeenactivated = false;
        private double _zoomDistance = 0;
        private const float ArmsDownMin = -0.20f;
        private readonly BarycenterCalculator _bCenterHelper = new BarycenterCalculator(30);
        private List<List<Joint>> _list = new List<List<Joint>>();

        private List<Joint> _listLHand = new List<Joint>();
        private List<Joint> _listRHand = new List<Joint>();
        private List<Joint> _listHead = new List<Joint>();

        private const int StorageNumber = 20;
        private readonly List<float> _stockForEnd = new List<float>();
        private const float MaxAllignementX = 0.2f;
        private const float MaxAllignementY = 0.1f;

        public ZoomManager()
        {
            _list.Add(_listLHand);
            _list.Add(_listRHand);
        }

        #region INotifyPropertyChanged

        /// <summary>
        /// Evénement déclenché lorsqu'un propriété change.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Déclenche l'événement <see cref="PropertyChanged"/>.
        /// </summary>
        /// <param name="propertyName">Le nom de la propriété.</param>
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion

        public void AnalyzeZoom(SkeletonData skeleton)
        {
            _bCenterHelper.Add(skeleton.Position.ToVector3X(), skeleton.TrackingID);
            if (!_bCenterHelper.IsStable(skeleton.TrackingID))
                return;
            foreach (Joint joint in skeleton.Joints)
            {
                if (joint.Position.W < 0.8f || joint.TrackingState != JointTrackingState.Tracked)
                    continue;
                switch (joint.ID)
                {
                    case JointID.HandLeft:
                        AddTolist(_list[0], joint);
                        break;
                    case JointID.HandRight:
                        AddTolist(_list[1], joint);
                        break;
                    case JointID.Head:
                        _listHead.Add(joint);
                        break;
                }
            }
            if (IsZoomActive || StartZoom())
            {
                IsZoomActive = true;
                _hasbeenactivated = true;
                _zoomDistance = GetZoomDistance();
                ZoomEvent.Invoke(_zoomDistance);
                LookEndOfZoom();
                return;
            }
        }

        public void LookResetOfZoom()
        {
            _stockForEnd.Clear();
            IsZoomActive = false;
            ZoomEvent.Invoke(-2);
            ZoomEvent.Invoke(-1);
            _hasbeenactivated = false;
            _list[0].Clear();
            _list[1].Clear();
        }

        private void LookEndOfZoom()
        {
            for (int i = 0; i < _list[0].Count; i++)
            {
                Debug.WriteLine("check " + _list[0][i].Position.Y.ToString());
                if (_list[0][i].Position.Y < ArmsDownMin)
                {
                    _stockForEnd.Clear();
                    IsZoomActive = false;
                    ZoomEvent.Invoke(-1);
                    return;
                }
            }
        }

        private double GetZoomDistance()
        {
            double res = 0;

            if (_list[0].Any() && _list[1].Any())
            {
                Joint rightHandJoint = _list[0].Last();
                Joint leftHandJoint = _list[1].Last();
                res = Math.Abs(rightHandJoint.Position.X - leftHandJoint.Position.X);
            }

            return res;
        }

        //verifie que les deux mains sont cote a cote
        private bool StartZoom()
        {
            bool x = false;
            bool y = false;

            if (!IsZoomActive)
            {
                int min = GetMin(_list[0], _list[1]);
                for (int i = 0; i < min; ++i)
                {
                    if (IsAlmostEqual(_list[0][i].Position.Y, _list[1][i].Position.Y, MaxAllignementY))
                    {
                        y = true;
                        break;
                    }
                }

                if (y)
                {
                    for (int i = 0; i < min; ++i)
                    {
                        if (IsAlmostEqual(_list[0][i].Position.X, _list[1][i].Position.X, MaxAllignementX))
                        {
                            _stockForEnd.Add(_list[0][i].Position.Y);
                            x = true;
                            break;
                        }
                    }
                }
            }
            return y && x;
        }

        private int GetMin(List<Joint> l1, List<Joint> l2)
        {
            var min = l1.Count;
            if (min > l2.Count)
                min = l2.Count;
            return min;
        }

        private bool IsAlmostEqual(float a, float b, float allignement)
        {
            float a1 = a + allignement;
            float a2 = a - allignement;
            if (b > a1 || b < a2)
                return false;
            return true;
        }


        private void AddTolist(List<Joint> list, Joint joint)
        {
            list.Add(joint);
            if (list.Count > StorageNumber)
                list.RemoveAt(0);
        }
    }

}